﻿using Arction.Wpf.Charting;
using Arction.Wpf.Charting.ChartManager;
using Arction.Wpf.Charting.SeriesXY;
using Prism.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using WPF_UI_Lightningchart.ViewModels;

namespace WPF_UI_Lightningchart.UserControls
{
    /// <summary>
    /// Spectrum2DControl.xaml 的交互逻辑
    /// </summary>
    public partial class Spectrum2DControl : UserControl
    {
        private SensorViewModel SensorViewModel;
        private Spectrogram2D _spectrograms2D;
        private DispatcherTimer _timerAutoYFit;
        private ChartManager _chartManager;


        private bool IsAutoFit = true;

        //参数
        private int _intervalFFT { get; set; } = 1000; // FFT间隔
        private int _channelCount { get; set; } = 1;
        private int _samplingFrequency { get; set; } = 1000; // 采样频率
        private int _resolution { get; set; } = 1024; // FFT窗口长度
        private int _topFrequency { get; set; } = 120; //最大频率

        
        public Spectrum2DControl(IEventAggregator eventAggregator)
        {
            InitializeComponent();
            this.Loaded += (r, s) =>
            {
                _chartManager = new ChartManager
                {
                    Name = "chartManager",
                    MemoryGarbageCollecting = true
                };
                _timerAutoYFit = new DispatcherTimer
                {
                    Interval = new TimeSpan(0, 0, 0, 0, 500)
                };
                _timerAutoYFit.Tick += new EventHandler(_timerAutoYFit_Tick);
                _timerAutoYFit.IsEnabled = true;
                InitFFTCharts();
            };
            this.Unloaded += (s, e) =>
            {
                Task.Run(() =>
                {
                    this.Dispatcher.Invoke(() =>
                    {
                        PerformDisposing();
                    });
                });
            };
        }

        private void Grid_DragOver(object sender, DragEventArgs e)
        {
            e.Effects = e.Data.GetDataPresent(typeof(SensorViewModel)) ? DragDropEffects.Move : DragDropEffects.None;
            e.Handled = true;
        }
        private void Grid_Drop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(SensorViewModel)))
            {
                if (SensorViewModel != null)
                {
                    InitFFTCharts();
                    SensorViewModel.FFTProcessor.OnFFTCalculated -= HandleSignalReaderNewPoints;
                }
                var sensorData = e.Data.GetData(typeof(SensorViewModel)) as SensorViewModel;
                SensorViewModel = (SensorViewModel)sensorData;
                SensorViewModel.FFTProcessor.OnFFTCalculated += HandleSignalReaderNewPoints;
                _intervalFFT = SensorViewModel._intervalFFT; // FFT间隔
                _channelCount = SensorViewModel._channelCount;
                _samplingFrequency = SensorViewModel._samplingFrequency; // 采样频率
                _resolution = SensorViewModel._resolution; // FFT窗口长度
                _topFrequency = SensorViewModel._topFrequency; //最大频率
                InitFFTCharts();
            }
        }

        private void HandleSignalReaderNewPoints(double[][][] xValues, double[][][] yValues, double speed)
        {
            Dispatcher.Invoke(new System.Action(() =>
            {
                int channelNumber = 0;
                int rows = xValues.Length;
                for (channelNumber = 0; channelNumber < _channelCount; channelNumber++)
                {
                    if (_spectrograms2D != null)
                    {
                        _spectrograms2D.SetData(yValues, channelNumber, rows);
                    }
                }
            }));
        }

        public void PerformDisposing()
        {
            _timerAutoYFit.Tick -= new EventHandler(_timerAutoYFit_Tick);
            _timerAutoYFit.IsEnabled = false;
            _timerAutoYFit.Stop();
            gridChartGrid?.Children.Clear();
            gridChartGrid = null;
            if (_spectrograms2D != null)
            {
                _spectrograms2D.Dispose();
                _spectrograms2D = null;
            }
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        #region FFT图
        /// <summary>
        /// 初始化FFT
        /// </summary>
        private void InitFFTCharts()
        {
            //清除释放资源

            if (_spectrograms2D != null)
            {
                _spectrograms2D.Dispose();
                _spectrograms2D = null;
            }
            int resolution = _resolution;
            //满足采样定理
            if (_topFrequency < _samplingFrequency / 2)resolution = (int)Math.Round((double)_topFrequency / (double)(_samplingFrequency) * _resolution);//FFT显示窗口长度

            double dFFTwinOffset = (double)(_resolution / 2) / _samplingFrequency;
            Spectrogram2D s2d =
                new Spectrogram2D(
                    gridChartGrid,
                    false,
                    resolution,
                    _intervalFFT,
                    8,
                    0,
                    _topFrequency,
                    dFFTwinOffset,
                    "P(f)\n - Channel " + (0).ToString(),
                    Colors.YellowGreen
                );

            s2d.Chart.ChartName = "Spectrogram " + (0).ToString();

            _spectrograms2D = s2d;

            ArrangeFFTCharts();
        }
        private void ArrangeFFTCharts()
        {
            if (_channelCount == 0)
                return;

            int totalWidth = (int)gridChartGrid.ActualWidth;
            int totalHeight = (int)gridChartGrid.ActualHeight;
            int height = totalHeight / _channelCount;
            int x = 0;
            int top = 0;
            _spectrograms2D.SetBounds();
            top += height;
        }
        private void _timerAutoYFit_Tick(object sender, EventArgs e)
        {
            FitY();
        }
        private void FitY()
        {
            if (_spectrograms2D != null && IsAutoFit == true)
            {
                _spectrograms2D.FitView();
            }
        }
        #endregion
    }

    public class Spectrogram2D
    {
        private LightningChartUltimate _chart;
        private int m_iSizeTimeResolution = 1;
        private int m_iFreqResolution = 1;
        private double m_dTimeStep = 0;
        private double m_dTimeRangeLengthSec = 1;
        private IntensityGridSeries _grid = null;
        private double[][] m_aValuesData;

        public Spectrogram2D(
            Panel parentControl,
            bool verticalScrolling,
            int resolution,
            double timeStepMs,         //50ms
            double timeRangeLengthSec,//默认5秒
            double freqMin,
            double freqMax,
            double dFFTtimeWinOffset,
            string title,
            Color toneColor
        )
        {
            double dDefaultYMax = 7000;

            _chart = new LightningChartUltimate
            {
                ChartName = "Spectrogram",
                VerticalAlignment = VerticalAlignment.Center,
                HorizontalAlignment = HorizontalAlignment.Center
            };

            parentControl.Children.Add(_chart);

            m_iFreqResolution = resolution;
            m_dTimeRangeLengthSec = timeRangeLengthSec;

            _chart.BeginUpdate();

            //Setup custom style

            _chart.Name = "Spectrogram";
            _chart.Title.Visible = false;
            _chart.Title.Font = new WpfFont("Segoe UI", 14, true, false);
            _chart.Title.Offset.SetValues(0, 0);
            _chart.ColorTheme = ColorTheme.LightGray;
            _chart.ChartBackground.Style = RectFillStyle.ColorOnly;
            _chart.ChartBackground.Color = Color.FromArgb(0, 0, 0, 0);
            _chart.ChartBackground.GradientColor = Color.FromArgb(0, 0, 0, 0);
            _chart.ChartBackground.GradientFill = GradientFill.Solid;

            _chart.ViewXY.Margins = new Thickness(70, 40, 15, 10);
            _chart.ViewXY.AxisLayout.AutoAdjustMargins = false;
            _chart.ViewXY.AxisLayout.XAxisAutoPlacement = XAxisAutoPlacement.TopThenBottom;
            _chart.ViewXY.GraphBackground.Style = RectFillStyle.ColorOnly;
            _chart.ViewXY.GraphBackground.Color = Color.FromArgb(0, 0, 0, 0);
            _chart.ViewXY.GraphBackground.GradientColor = Color.FromArgb(0, 0, 0, 0);
            _chart.ViewXY.GraphBackground.GradientFill = GradientFill.Solid;




            _chart.ViewXY.XAxes[0].ValueType = AxisValueType.Number;
            _chart.ViewXY.XAxes[0].LabelsFont = new WpfFont("Segoe UI", 13, false, false);
            _chart.ViewXY.XAxes[0].Title.Text = "频率";
            _chart.ViewXY.XAxes[0].Title.Visible = true;

            _chart.ViewXY.YAxes[0].ValueType = AxisValueType.Time;
            _chart.ViewXY.YAxes[0].LabelsFont = new WpfFont("Segoe UI", 13, false, false);
            _chart.ViewXY.YAxes[0].Title.Text = "Time";
            _chart.ViewXY.YAxes[0].Title.Visible = true;

            _chart.ViewXY.LegendBoxes[0].SeriesTitleColor = toneColor;
            _chart.ViewXY.LegendBoxes[0].ValueLabelColor = Colors.White;
            _chart.ViewXY.LegendBoxes[0].IntensityScales.ScaleBorderColor = Colors.White;
            _chart.ViewXY.LegendBoxes[0].Position = LegendBoxPositionXY.RightCenter;
            _chart.ViewXY.LegendBoxes[0].Layout = LegendBoxLayout.Vertical;
            _chart.ViewXY.LegendBoxes[0].Offset.SetValues(-20, 0);
            _chart.ViewXY.LegendBoxes[0].Fill.Style = RectFillStyle.None;
            _chart.ViewXY.LegendBoxes[0].Shadow.Visible = false;
            _chart.ViewXY.LegendBoxes[0].BorderWidth = 0;
            _chart.ViewXY.LegendBoxes[0].IntensityScales.ScaleSizeDim1 = 100;
            _chart.ViewXY.LegendBoxes[0].IntensityScales.ScaleSizeDim2 = 15;
            _chart.ViewXY.LegendBoxes[0].UnitsColor = Color.FromArgb(0, 0, 0, 0);
            
            m_dTimeStep = timeStepMs / 1000.0;
            double dTimeAxisMax = dFFTtimeWinOffset;
            double dTimeAxisMin = m_dTimeStep - m_dTimeRangeLengthSec + dTimeAxisMax;
            _chart.ViewXY.XAxes[0].SetRange(freqMin, freqMax);
            _chart.ViewXY.YAxes[0].SetRange(dTimeAxisMin, dTimeAxisMax);

            m_iSizeTimeResolution = (int)Math.Round(timeRangeLengthSec / (timeStepMs / 1000.0));//图表显示缓存数据数量，默认5/0.05=100

            //Create external values data array that represents the grid contents
            m_aValuesData = new double[m_iSizeTimeResolution][];
            for (int row = 0; row < m_iSizeTimeResolution; row++)
            {
                m_aValuesData[row] = new double[resolution]; //resolution=8192 初始化一个三维数组，每个二维数组的大小为显示的点数大小，默认8192
            }

            _grid = new IntensityGridSeries(_chart.ViewXY, _chart.ViewXY.XAxes[0], _chart.ViewXY.YAxes[0])
            {
                ContourLineType = ContourLineTypeXY.None,
                WireframeType = SurfaceWireframeType.None,
                PixelRendering = true,
            };
            _grid.Title.Text = "P(f)";
            _grid.SetValuesData(m_aValuesData, IntensityGridValuesDataOrder.RowsColumns);
            _grid.Data = null;
            _grid.SetRangesXY(_chart.ViewXY.XAxes[0].Minimum, _chart.ViewXY.XAxes[0].Maximum,
                _chart.ViewXY.YAxes[0].Minimum, _chart.ViewXY.YAxes[0].Maximum);

            _grid.ValueRangePalette = CreatePalette(_grid, dDefaultYMax, toneColor);

            _chart.ViewXY.IntensityGridSeries.Add(_grid);//强度网格

            _chart.EndUpdate();
        }

        public LightningChartUltimate Chart
        {
            get
            {
                return _chart;
            }
        }

        private ValueRangePalette CreatePalette(IntensityGridSeries ownerSeries, double yRange)//值范围调色板（固定颜色）
        {
            ValueRangePalette palette = new ValueRangePalette(ownerSeries);
            palette.Steps.Clear();
            palette.Steps.Add(new PaletteStep(palette, Color.FromArgb(0, 0, 0, 0), 0));
            palette.Steps.Add(new PaletteStep(palette, Colors.Gold, 100.0 * yRange / 100.0));
            palette.Type = PaletteType.Gradient;
            return palette;
        }

        private ValueRangePalette CreatePalette(IntensityGridSeries ownerSeries, double yRange, Color baseColor)//值范围调色板（颜色变量）
        {
            ValueRangePalette palette = new ValueRangePalette(ownerSeries);
            palette.Steps.Clear();
            palette.Steps.Add(new PaletteStep(palette, Color.FromArgb(0, 0, 0, 0), 0));
            palette.Steps.Add(new PaletteStep(palette, baseColor, 40.0 * yRange / 100.0));
            palette.Steps.Add(new PaletteStep(palette, Colors.Black, 100.0 * yRange / 100.0));
            palette.Type = PaletteType.Gradient;
            return palette;
        }

        public void Dispose()
        {
            if (_grid != null)
            {
                _grid.Dispose();
                _grid = null;
            }

            if (_chart != null)
            {
                _chart.Dispose();
                _chart = null;
            }
        }

        public void FitView()
        {
            int columns = m_iFreqResolution;
            int rows = m_iSizeTimeResolution;

            double minY = double.MaxValue;
            double maxY = double.MinValue;
            double y;

            _chart.BeginUpdate();

            if (_grid != null)
            {
                for (int iCol = 0; iCol < columns; iCol++)
                {
                    for (int row = 0; row < rows; row++)
                    {
                        y = m_aValuesData[row][iCol];
                        if (y > maxY)
                        {
                            maxY = y;
                        }

                        if (y < minY)
                        {
                            minY = y;
                        }
                    }
                }
                _grid.ValueRangePalette = CreatePalette(_grid, maxY);
            }

            _chart.EndUpdate();
        }

        public void SetData(double[][][] yValues, int channelIndex, int rowCount)
        {
            if (_chart == null)
            {
                return;
            }

            //Console.WriteLine("打印 yValues 内容:");
            //for (int i = 0; i < yValues.Length; i++)
            //{
            //    Console.WriteLine($"通道 {i + 1} 的数据:");
            //    for (int j = 0; j < yValues[i].Length; j++)
            //    {
            //        // 打印每个通道的 FFT 结果值，以逗号分隔
            //        string rowValues = string.Join(", ", yValues[i][j].Select(val => val.ToString("F4")));
            //        Console.WriteLine($"频率索引 {j}: {rowValues}");
            //    }
            //}

            _chart.BeginUpdate();

            for (int row = 0; row < rowCount; row++)
            {
                double[] aYValues = yValues[row][channelIndex];

                Array.Resize(ref yValues, m_iFreqResolution);

                for (int iTimeSlot = 1; iTimeSlot < m_iSizeTimeResolution; iTimeSlot++)
                {
                    m_aValuesData[iTimeSlot - 1] = m_aValuesData[iTimeSlot]; //change the reference
                }

                m_aValuesData[m_iSizeTimeResolution - 1] = aYValues;
            }

            _grid.InvalidateValuesDataOnly();

            double dCurrentYMin = _chart.ViewXY.YAxes[0].Minimum;
            double dTotalTimeShift = m_dTimeStep * rowCount;

            _chart.ViewXY.YAxes[0].SetRange(dCurrentYMin + dTotalTimeShift, dCurrentYMin + dTotalTimeShift + m_dTimeRangeLengthSec);

            _grid.SetRangesXY(_chart.ViewXY.XAxes[0].Minimum, _chart.ViewXY.XAxes[0].Maximum,
                _chart.ViewXY.YAxes[0].Minimum, _chart.ViewXY.YAxes[0].Maximum);

            _chart.EndUpdate();
        }

        public void SetBounds( )
        {
            _chart.Margin = new Thickness(10);
            _chart.HorizontalAlignment = HorizontalAlignment.Stretch;
            _chart.VerticalAlignment = VerticalAlignment.Stretch;
        }
    }
}
